package com.maker.service.impl;
import com.google.gson.Gson;
import com.google.gson.internal.LinkedTreeMap;
import com.maker.core.AlipayTemplate;
import com.maker.entity.OrderInfo;
import com.maker.entity.PaymentInfo;
import com.maker.entity.RefundInfo;
import com.maker.enums.OrderStatus;
import com.maker.enums.PayType;
import com.maker.enums.alipay.AlipayTradeState;
import com.maker.service.AlipayDevelopService;
import com.maker.service.OrderInfoRepository;
import com.maker.service.PaymentInfoRepository;
import com.maker.service.RefundInfoRepository;
import com.maker.utils.OrderNoUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.transaction.Transactional;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;

@Service
@Slf4j
public class AlipayDevelopServiceImpl implements AlipayDevelopService {

    @Resource
    private OrderInfoService orderInfoService;
    @Resource
    private AlipayTemplate alipayTemplate;
    @Resource
    private OrderInfoRepository orderInfoRepository;
    @Resource
    private PaymentInfoRepository paymentInfoRepository;
    @Resource
    private RefundInfoRepository refundInfoRepository;
    @Resource
    Environment conig;

    private final ReentrantLock lock = new ReentrantLock();
    @Transactional
    @Override
    public String trateCreate(Long productId) {
        log.info("========生成订单========");
        //生成订单
        OrderInfo orderInfo = orderInfoService.verifyOrderIsExist(productId);
       //调用支付宝接口
       String  respData =  alipayTemplate.createOrder(orderInfo.getTotalFee(),orderInfo.getTitle(),orderInfo.getOrderNo());
        return respData;
    }


    @Override
    public String rsaCheckV1(Map<String, String> params) {
        String result="failure";
        Boolean flag = alipayTemplate.rsaCheckV1(params);
        if(!flag){
            // TODO 验签失败则记录异常日志，并在response中返回failure.
            log.error("支付成功异步通知验签失败！");
            return result;
        }
        // TODO 验签成功后，按照支付结果异步通知中的描述，对支付结果中的业务内容进行二次校验，
        log.info("支付成功异步通知验签成功！");
//        1. 商家需要验证该通知数据中的 out_trade_no 是否为商家系统中创建的订单号。
        String out_trade_no = params.get("out_trade_no");
        OrderInfo orderInfos = orderInfoRepository.getOrderInfoByOrderNo(out_trade_no, 11L);
        if(orderInfos==null){
            log.error("订单不存在！");
            return result;
        }
//        2. 判断 total_amount 是否确实为该订单的实际金额（即商家订单创建时的金额）。
        String totalAmount = params.get("total_amount");
        int alipay_money = new BigDecimal(totalAmount).multiply(new BigDecimal("100")).intValue();
        int database_money = orderInfos.getTotalFee().intValue();
        if(alipay_money!=database_money){
            log.error("金额校验失败！");
            return result;
        }
//        3. 校验通知中的 seller_id（或者 seller_email）是否为 out_trade_no 这笔单据的对应的操作方（有的时候，一个商家可能有多个 seller_id/seller_email）。
        String sellerId = params.get("seller_id");
        String sellerIdProperty = conig.getProperty("alipay.seller-id");
        if(!sellerId.equals(sellerIdProperty)){
            log.error("商家pid校验失败！");
            return result;
        }
//        4. 验证 app_id 是否为该商家本身
        String appId=params.get("app_id");
        String appIdProperty = conig.getProperty("alipay.app-id");
        if(!appId.equals(appIdProperty)){
            log.error("应用ID校验失败！");
            return result;
        }
        //在支付宝的业务通知中，只有交易通知状态为 TRADE_SUCCESS 或 TRADE_FINISHED 时，
        // 支付宝才会认定为买家付款成功
        String  tradeStatus = params.get("trade_status");
        if(!"TRADE_SUCCESS".equals(tradeStatus)){
            log.error("付款失败！");
            return result;
        }
        //  校验成功后在response中返回success并继续商户自身业务处理，校验失败返回failure
        //修改订单状态  记录操作日志
        this.processOrder(params);
        result="success";
        return result;
    }
    @Transactional
    @Override
    public void processOrder(Map<String, String> params) {
        //更新订单状态
        String out_trade_no = params.get("out_trade_no");
        //解决高并发问题
        if(lock.tryLock()){
            try {
                OrderInfo orderInfos = orderInfoRepository.getOrderInfoByOrderNo(out_trade_no, 11L);

                //解决重复通知   导致的日志记录多次问题
                //接口调用的幂等性：无论接口调用多少次，以下业务只执行一次
//                OrderInfo infoByOrderNo = orderInfoRepository.getOrderInfoByOrderNo(out_trade_no,11L);
                if(!OrderStatus.NOTPAY.getType().equals(orderInfos.getOrderStatus())){
                    log.info("处理重复通知，解决日志重复记录处理");
                    return;
                }
                log.info("更新订单状态");
                orderInfos.setOrderStatus(OrderStatus.SUCCESS.getType());
                Timestamp now = new Timestamp(System.currentTimeMillis());
                orderInfos.setUpdateTime(now);
                orderInfoRepository.save(orderInfos);
                //记录支付宝支付日志
                this.processOrderLogForAlipay(params);
            } finally {
                lock.unlock();
            }
        }

    }

    @Override
    public void processOrderLogForAlipay(Map<String, String> params) {
        log.info("记录支付日志");
        String out_trade_no = params.get("out_trade_no");
        //支付宝交易号
        String transactionId = params.get("trade_no");
        String totalAmount = params.get("total_amount");
        String  tradeStatus = params.get("trade_status");
        int alipay_money = new BigDecimal(totalAmount).multiply(new BigDecimal("100")).intValue();
        PaymentInfo  paymentInfo = new PaymentInfo();
        paymentInfo.setOrderNo(out_trade_no);
        paymentInfo.setPaymentType(PayType.ALIPAY.getType());
        paymentInfo.setTradeState(tradeStatus);
        paymentInfo.setTransactionId(transactionId);
        paymentInfo.setTradeType("电脑PC端交易");
        paymentInfo.setPayerTotal(alipay_money);

        Gson gson = new Gson();
        String json = gson.toJson(params, Map.class);
        paymentInfo.setContent(json);

        paymentInfoRepository.save(paymentInfo);

    }

    /**
     * 取消订单
     * @param orderNo
     */
    @Override
    public void cancelOrder(String orderNo) {

        //调用支付宝提供的统一收单交易关闭接口
        alipayTemplate.closeOrder(orderNo);
        //更新用户订单状态
        OrderInfo orderInfos = orderInfoRepository.getOrderInfoByOrderNo(orderNo, 11L);
        orderInfos.setOrderStatus(OrderStatus.CANCELL.getType());
        Timestamp now = new Timestamp(System.currentTimeMillis());
        orderInfos.setUpdateTime(now);
        orderInfoRepository.save(orderInfos);

    }

    /**
     * 查询订单状态接口
     * @param orderNo  订单号
     */
    @Override
    public String queryOrder(String orderNo) {
        String response = alipayTemplate.queryOrder(orderNo);
       return  response;
    }

    public String queryRefundsOrder(String orderNo) {
        String response = alipayTemplate.queryRefunds(orderNo);
        return  response;
    }

    /**
     * 根据订单编号调用支付宝接口查单，核实订单状态
     * 如果订单未创建，则更新商户端订单状态
     * 如果订单未支付，则调用关单接口关闭订单，并更新商户端订单状态
     * 如果订单已支付，则更新商户端订单状态，并记录支付日志
     * @param orderNo
     */
    public void checkOrderStatus(String orderNo){

        log.warn("根据订单号核实订单状态===》{}",orderNo);

        String result = this.queryOrder(orderNo);
        if(result==null){
            log.info("订单未创建，则更新商户端订单状态");
            OrderInfo orderInfos = orderInfoRepository.getOrderInfoByOrderNo(orderNo, 11L);
            orderInfos.setOrderStatus(OrderStatus.Closed.getType());
            Timestamp now = new Timestamp(System.currentTimeMillis());
            orderInfos.setUpdateTime(now);
            orderInfoRepository.save(orderInfos);
            return;
        }
        //如果订单未支付，则调用关单接口关闭订单，并更新商户端订单状态

        Gson gson = new Gson();
        HashMap<String, LinkedTreeMap>  resultMap = gson.fromJson(result, HashMap.class);
        log.info("查询该订单在支付宝的状态,订单号={}，alipay_trade_query_response={}",orderNo,resultMap);
        LinkedTreeMap alipay_trade_query_response = resultMap.get("alipay_trade_query_response");
        String trade_status = (String)alipay_trade_query_response.get("trade_status");
        if(!StringUtils.hasText(trade_status)){
            log.error("该订单交易状态在支付宝端异常，即trade_status={}",trade_status);
            return;
        }
        if(AlipayTradeState.NOTPAY.getType().equals(trade_status)){
            log.info("订单未支付，则调用关单接口关闭订单，并更新商户端订单状态,订单号：{}",orderNo);
            alipayTemplate.closeOrder(orderNo);
            //更新商户端订单状态
            OrderInfo orderInfos = orderInfoRepository.getOrderInfoByOrderNo(orderNo, 11L);
            orderInfos.setOrderStatus(OrderStatus.Closed.getType());
            Timestamp now = new Timestamp(System.currentTimeMillis());
            orderInfos.setUpdateTime(now);
            orderInfoRepository.save(orderInfos);
            return;
        }
        if(AlipayTradeState.SUCCESS.getType().equals(trade_status)){
            log.info("订单已支付，则更新商户端订单状态，并记录支付日志,订单号：{}",orderNo);
            //更新商户端订单状态
            OrderInfo orderInfos = orderInfoRepository.getOrderInfoByOrderNo(orderNo, 11L);
            orderInfos.setOrderStatus(OrderStatus.SUCCESS.getType());
            Timestamp now = new Timestamp(System.currentTimeMillis());
            orderInfos.setUpdateTime(now);
            orderInfoRepository.save(orderInfos);
            //记录支付宝支付日志
            this.processOrderLogForAlipay(alipay_trade_query_response);
            return;
        }

    }
    /**
     * 根据订单编号调用支付宝接口已支付查单，核实订单状态
     * 如果退单未创建，则更不需要操作
     * 如果状态退款成功，则调用关单接口关闭订单，并更新商户端订单状态
     * 如果状态退款失败，则更新商户端订单状态，并记录支付日志
     * @param orderNo
     */
    @Override
    public void checkRefundOrderStatus(String orderNo) {
        log.warn("根据订单号核实退单状态===》{}",orderNo);

        String result = this.queryRefundsOrder(orderNo);
        if(result==null){
            log.info("退单未创建，表示该订单未退款，请核实该订单");
            return;
        }
        //如果订单未支付，则调用关单接口关闭订单，并更新商户端订单状态
        Gson gson = new Gson();
        HashMap<String, LinkedTreeMap>  resultMap = gson.fromJson(result, HashMap.class);
        log.info("查询该退款-订单在支付宝的状态,订单号={}，alipay_trade_fastpay_refund_query_response={}",orderNo,resultMap);
        LinkedTreeMap alipay_trade_fastpay_refund_query_response = resultMap.get("alipay_trade_fastpay_refund_query_response");
        String trade_status = (String)alipay_trade_fastpay_refund_query_response.get("refund_status");
        if(!StringUtils.hasText(trade_status)){
            log.error("该退款订单交易状态在支付宝端异常，即trade_status={}",trade_status);
            return;
        }
        if(AlipayTradeState.REFUND_SUCCESS.getType().equals(trade_status)){
            //退款成功处理
            OrderInfo orderInfo = orderInfoRepository.getOrderInfoByOrderNo(orderNo, 11L);
            orderInfo.setOrderStatus(OrderStatus.REFUND_SUCCESS.getType());
            Timestamp now = new Timestamp(System.currentTimeMillis());
            orderInfo.setUpdateTime(now);
            orderInfoRepository.save(orderInfo);
            //更新退款单
            RefundInfo refundInfo = refundInfoRepository.getRefundInfoByOrderNo(orderNo);
            refundInfo.setRefundStatus(OrderStatus.REFUND_SUCCESS.getType());
            refundInfoRepository.save(refundInfo);

        }else {
            //退款失败处理
            log.info("支付宝服务器状态为退款失败，需要TODO用户端处理。该过程结合自身业务情况进行处理。");
//            OrderInfo orderInfo = orderInfoRepository.getOrderInfoByOrderNo(orderNo, 11L);
//            orderInfo.setOrderStatus(OrderStatus.REFUND_ABNORMAL.getType());
//            Timestamp now = new Timestamp(System.currentTimeMillis());
//            orderInfo.setUpdateTime(now);
//            orderInfoRepository.save(orderInfo);
//            //更新退款单状态
//            //更新退款单
//            RefundInfo refundInfo = refundInfoRepository.getRefundInfoByOrderNo(orderNo);
//            refundInfo.setRefundStatus(OrderStatus.REFUND_ABNORMAL.getType());
//            refundInfoRepository.save(refundInfo);

        }
    }

    @Override
    public RefundInfo refund(String orderNo,String reason) {
        log.info("调用退款API");
        //创建退款单
        RefundInfo refundInfo = this.createRefundByOrderNoForAlipay(orderNo, reason);
        String response = alipayTemplate.refunds(orderNo, refundInfo.getRefund(), reason);
        if (StringUtils.hasText(response)){
            OrderInfo orderInfos = orderInfoRepository.getOrderInfoByOrderNo(orderNo, 11L);
            orderInfos.setOrderStatus(OrderStatus.REFUND_SUCCESS.getType());
            Timestamp now = new Timestamp(System.currentTimeMillis());
            orderInfos.setUpdateTime(now);
            orderInfoRepository.save(orderInfos);
            //更新退款单
            refundInfo.setRefundStatus(OrderStatus.REFUND_SUCCESS.getType());
            refundInfo.setContentReturn(response);
            refundInfoRepository.save(refundInfo);

        }else {
            //更新订单状态
            OrderInfo orderInfos = orderInfoRepository.getOrderInfoByOrderNo(orderNo, 11L);
            orderInfos.setOrderStatus(OrderStatus.REFUND_ABNORMAL.getType());
            Timestamp now = new Timestamp(System.currentTimeMillis());
            orderInfos.setUpdateTime(now);
            orderInfoRepository.save(orderInfos);
            //更新退款单状态
            refundInfo.setRefundStatus(OrderStatus.REFUND_ABNORMAL.getType());
            refundInfoRepository.save(refundInfo);

        }
        return refundInfo;
    }

    @Override
    public String queryTradeBill(String billdate, String type) {
        return alipayTemplate.queryBill(billdate,type);
    }

    public RefundInfo createRefundByOrderNoForAlipay(String orderNo, String reason){
        //根据订单编号获取订单信息
        OrderInfo orderInfo = orderInfoRepository.getOrderInfoByOrderNo(orderNo, 11L);

        //根据订单单号生成退款订单
        RefundInfo  refundInfo =new RefundInfo();
        refundInfo.setOrderNo(orderNo);
        refundInfo.setRefundNo(OrderNoUtils.getRefundNo());
        refundInfo.setTotalFee(orderInfo.getTotalFee());
        refundInfo.setRefund(orderInfo.getTotalFee());
        refundInfo.setReason(reason);
        Timestamp now = new Timestamp(System.currentTimeMillis());
        refundInfo.setCreateTime(now);
        refundInfoRepository.save(refundInfo);
        return refundInfo;
    }


}
